package drds.global_transaction.global_transaction_manager.api;

import drds.common.ShouldNeverHappenException;
import drds.global_transaction.api.exception.GlobalTransactionException;
import drds.global_transaction.common.thread.NamedThreadFactory;
import drds.global_transaction.global_transaction_manager.TransactionManagerClient;
import drds.global_transaction.resource_manager.ResourceManagerClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class DemoCode
{

	private static class GlobalTransactionFailureHandler implements drds.global_transaction.global_transaction_manager.api.GlobalTransactionFailureHandler
	{

		private static final Logger LOGGER = LoggerFactory.getLogger(GlobalTransactionFailureHandler.class);

		@Override
		public void onGlobalTransactionBeginFailure(GlobalTransaction globalTransaction, Throwable cause)
		{
			LOGGER.warn("failed to globalTransactionBegin transaction. ", cause);

		}

		@Override
		public void onGlobalTransactionCommitFailure(final GlobalTransaction globalTransaction, Throwable cause)
		{
			LOGGER.warn("failed to globalTransactionCommit transaction[" + globalTransaction.getGlobalTransactionIdWithAddressAndPort() + "]", cause);
			final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("BusinessRetryCommit", 1, true));
			scheduledExecutorService.scheduleAtFixedRate(new Runnable()
			{
				@Override
				public void run()
				{
					try
					{
						globalTransaction.commit();
						scheduledExecutorService.shutdownNow();

					} catch (GlobalTransactionException ignore)
					{

					}

				}
			}, 0, 5, TimeUnit.SECONDS);

		}

		@Override
		public void onGlobalTransactionRollbackFailure(final GlobalTransaction globalTransaction, Throwable cause)
		{
			LOGGER.warn("failed to globalTransactionBegin transaction[" + globalTransaction.getGlobalTransactionIdWithAddressAndPort() + "]", cause);
			final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("BusinessRetryRollback", 1, true));
			scheduledExecutorService.scheduleAtFixedRate(new Runnable()
			{
				@Override
				public void run()
				{
					try
					{
						globalTransaction.rollback();
						scheduledExecutorService.shutdownNow();

					} catch (GlobalTransactionException ignore)
					{

					}

				}
			}, 0, 5, TimeUnit.SECONDS);

		}

	}

	private static void businessCall1()
	{

	}

	private static void businessCall2()
	{

	}

	private static void businessCall3()
	{

	}

	/**
	 * Demo transactionExceptionCode for High Level API (TransactionTemplate) usage.
	 *
	 * @throws Throwable business exception
	 */
	public void demoByHighLevelAPI() throws Throwable
	{
		// 0. init
		init();

		// 0.1 prepare for the template instance
		GlobalTransactionTemplate globalTransactionTemplate = new GlobalTransactionTemplate();

		// 0.2 prepare for the failure resourceManagerInboundHandler (this is optional)
		drds.global_transaction.global_transaction_manager.api.GlobalTransactionFailureHandler globalTransactionFailureHandler = new GlobalTransactionFailureHandler();

		try
		{
			// run you business in template
			globalTransactionTemplate.execute(new GlobalTransactionExecutor()
			{
				@Override
				public Object execute() throws Throwable
				{
					// Do Your BusinessService
					businessCall1();
					businessCall2();
					businessCall3();
					return null;
				}

				@Override
				public String name()
				{
					return "my_tx_instance";
				}

				@Override
				public int timeout()
				{
					return 30000;
				}
			});
		} catch (GlobalTransactionExecutionException e)
		{
			GlobalTransactionExecutionExceptionCode globalTransactionExecutionExceptionCode = e.getGlobalTransactionExecutionExceptionCode();
			switch (globalTransactionExecutionExceptionCode)
			{
			case rollback_global_transaction_done:
				throw e.getOriginalException();
			case begin_global_transaction_failure:
				globalTransactionFailureHandler.onGlobalTransactionBeginFailure(e.getGlobalTransaction(), e.getCause());
				throw e.getCause();
			case commit_global_transaction_failure:
				globalTransactionFailureHandler.onGlobalTransactionCommitFailure(e.getGlobalTransaction(), e.getCause());
				throw e.getCause();
			case rollback_global_transaction_failure:
				globalTransactionFailureHandler.onGlobalTransactionRollbackFailure(e.getGlobalTransaction(), e.getCause());
				throw e.getCause();
			default:
				throw new ShouldNeverHappenException();

			}
		}

	}

	/**
	 * Demo transactionExceptionCode for Low Level API (GlobalTransaction) usage.
	 *
	 * @throws Throwable business exception
	 */
	public void demoByLowLevelAPI() throws Throwable
	{
		// 0. init
		init();

		// 1. get or create a transaction
		GlobalTransaction globalTransaction = GlobalTransactionContext.getCurrentOrCreateGlobalTransaction();

		// 2. globalTransactionBegin transaction
		try
		{
			globalTransaction.begin(30000, "my_tx_instance");

		} catch (GlobalTransactionException txe)
		{
			// TODO: Handle the transaction globalTransactionBegin failure.

		}

		Object rs = null;
		try
		{

			// Do Your BusinessService
			businessCall1();
			businessCall2();
			businessCall3();

		} catch (Throwable ex)
		{

			// 3. any business exception, globalTransactionRollback.
			try
			{
				globalTransaction.rollback();

				// 3.1 throw the business exception out.
				throw ex;

			} catch (GlobalTransactionException txe)
			{
				// TODO: Handle the transaction globalTransactionRollback failure.

			}

		}

		// 4. everything is fine, globalTransactionCommit.
		try
		{
			globalTransaction.commit();

		} catch (GlobalTransactionException txe)
		{
			// TODO: Handle the transaction globalTransactionRollback failure.

		}
	}

	private void init()
	{
		String applicationId = "my_app";
		String transactionServiceGroup = "my_tx_group";
		TransactionManagerClient.init(applicationId, transactionServiceGroup);
		ResourceManagerClient.init(applicationId, transactionServiceGroup);

	}
}
